{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "_cell_guid": "01e3b8c5-7e10-450b-a663-08c829eb793e",
    "_uuid": "9ae1e73615b56d28a06645128661dfa9e817f9c7"
   },
   "source": [
    "This code is inspirated by https://www.kaggle.com/keegil/keras-u-net-starter-lb-0-277"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "a3a5212a-bc80-485a-9109-e5be9b05f546",
    "_uuid": "bcb8165daaa0a560b300be8bcf4e4f9b31bfa472",
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import os\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "def make_df(train_path, test_path, img_size):\n",
    "    train_ids = next(os.walk(train_path))[1]\n",
    "    test_ids = next(os.walk(test_path))[1]\n",
    "    X_train = np.zeros((len(train_ids), img_size, img_size, 3), dtype=np.uint8)\n",
    "    Y_train = np.zeros((len(train_ids), img_size, img_size, 1), dtype=np.bool)\n",
    "    for i, id_ in enumerate(train_ids):\n",
    "        path = train_path + id_\n",
    "        img = cv2.imread(path + '/images/' + id_ + '.png')\n",
    "        img = cv2.resize(img, (img_size, img_size))\n",
    "        X_train[i] = img\n",
    "        mask = np.zeros((img_size, img_size, 1), dtype=np.bool)\n",
    "        for mask_file in next(os.walk(path + '/masks/'))[2]:\n",
    "            mask_ = cv2.imread(path + '/masks/' + mask_file, 0)\n",
    "            mask_ = cv2.resize(mask_, (img_size, img_size))\n",
    "            mask_ = mask_[:, :, np.newaxis]\n",
    "            mask = np.maximum(mask, mask_)\n",
    "        Y_train[i] = mask\n",
    "    X_test = np.zeros((len(test_ids), img_size, img_size, 3), dtype=np.uint8)\n",
    "    sizes_test = []\n",
    "    for i, id_ in enumerate(test_ids):\n",
    "        path = test_path + id_\n",
    "        img = cv2.imread(path + '/images/' + id_ + '.png')\n",
    "        sizes_test.append([img.shape[0], img.shape[1]])\n",
    "        img = cv2.resize(img, (img_size, img_size))\n",
    "        X_test[i] = img\n",
    "\n",
    "    return X_train, Y_train, X_test, sizes_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "_cell_guid": "27f0fe04-cd5b-4042-a2e0-eb9e301933f0",
    "_uuid": "bf6df4dfbbaddbd3d40293f3b05bf68ee51d4475"
   },
   "source": [
    "Define Unet model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "1960ba32-cdec-4aec-b39b-86e832940976",
    "_uuid": "8318ccbe407bf32eeba9723a597556cc634465ff",
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.layers.convolutional import Conv2D, Conv2DTranspose\n",
    "from keras.layers.pooling import MaxPooling2D\n",
    "from keras.layers.merge import concatenate\n",
    "from keras.models import Model\n",
    "from keras.layers import Input\n",
    "from keras.layers.core import Dropout, Lambda\n",
    "\n",
    "\n",
    "def Unet(img_size):\n",
    "    inputs = Input((img_size, img_size, 3))\n",
    "    s = Lambda(lambda x: x / 255)(inputs)\n",
    "\n",
    "    c1 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(s)\n",
    "    c1 = Dropout(0.1)(c1)\n",
    "    c1 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c1)\n",
    "    p1 = MaxPooling2D((2, 2))(c1)\n",
    "\n",
    "    c2 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(p1)\n",
    "    c2 = Dropout(0.1)(c2)\n",
    "    c2 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c2)\n",
    "    p2 = MaxPooling2D((2, 2))(c2)\n",
    "\n",
    "    c3 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(p2)\n",
    "    c3 = Dropout(0.2)(c3)\n",
    "    c3 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c3)\n",
    "    p3 = MaxPooling2D((2, 2))(c3)\n",
    "\n",
    "    c4 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(p3)\n",
    "    c4 = Dropout(0.2)(c4)\n",
    "    c4 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c4)\n",
    "    p4 = MaxPooling2D(pool_size=(2, 2))(c4)\n",
    "\n",
    "    c5 = Conv2D(256, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(p4)\n",
    "    c5 = Dropout(0.3)(c5)\n",
    "    c5 = Conv2D(256, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c5)\n",
    "\n",
    "    u6 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5)\n",
    "    u6 = concatenate([u6, c4])\n",
    "    c6 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(u6)\n",
    "    c6 = Dropout(0.2)(c6)\n",
    "    c6 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c6)\n",
    "\n",
    "    u7 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6)\n",
    "    u7 = concatenate([u7, c3])\n",
    "    c7 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(u7)\n",
    "    c7 = Dropout(0.2)(c7)\n",
    "    c7 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c7)\n",
    "\n",
    "    u8 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7)\n",
    "    u8 = concatenate([u8, c2])\n",
    "    c8 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(u8)\n",
    "    c8 = Dropout(0.1)(c8)\n",
    "    c8 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c8)\n",
    "\n",
    "    u9 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8)\n",
    "    u9 = concatenate([u9, c1], axis=3)\n",
    "    c9 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(u9)\n",
    "    c9 = Dropout(0.1)(c9)\n",
    "    c9 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same')(c9)\n",
    "\n",
    "    outputs = Conv2D(1, (1, 1), activation='sigmoid')(c9)\n",
    "\n",
    "    model = Model(inputs=[inputs], outputs=[outputs])\n",
    "\n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "_cell_guid": "475c0fc2-ca32-4db9-9008-05f675696a0e",
    "_uuid": "bebc9fc8e555aaba633741c0452fcfa0925664b4"
   },
   "source": [
    "Define generator. Using keras ImageDataGenerator.\n",
    "You can change the method of data augmentation by changing data_gen_args."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "b2a782ca-1656-4c4b-b320-4212989c530d",
    "_uuid": "487865a51b4d4c28bb69b88d4e5e20bc6b7bba80",
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "\n",
    "\n",
    "def generator(xtr, xval, ytr, yval, batch_size):\n",
    "    data_gen_args = dict(horizontal_flip=True,\n",
    "                         vertical_flip=True,\n",
    "                         rotation_range=90.,\n",
    "                         width_shift_range=0.1,\n",
    "                         height_shift_range=0.1,\n",
    "                         zoom_range=0.1)\n",
    "    image_datagen = ImageDataGenerator(**data_gen_args)\n",
    "    mask_datagen = ImageDataGenerator(**data_gen_args)\n",
    "    image_datagen.fit(xtr, seed=7)\n",
    "    mask_datagen.fit(ytr, seed=7)\n",
    "    image_generator = image_datagen.flow(xtr, batch_size=batch_size, seed=7)\n",
    "    mask_generator = mask_datagen.flow(ytr, batch_size=batch_size, seed=7)\n",
    "    train_generator = zip(image_generator, mask_generator)\n",
    "\n",
    "    val_gen_args = dict()\n",
    "    image_datagen_val = ImageDataGenerator(**val_gen_args)\n",
    "    mask_datagen_val = ImageDataGenerator(**val_gen_args)\n",
    "    image_datagen_val.fit(xval, seed=7)\n",
    "    mask_datagen_val.fit(yval, seed=7)\n",
    "    image_generator_val = image_datagen_val.flow(xval, batch_size=batch_size, seed=7)\n",
    "    mask_generator_val = mask_datagen_val.flow(yval, batch_size=batch_size, seed=7)\n",
    "    val_generator = zip(image_generator_val, mask_generator_val)\n",
    "\n",
    "    return train_generator, val_generator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "_cell_guid": "c9d9d672-adfc-4e79-a3fa-26d206e5e6d2",
    "_uuid": "fa55995ed9b64862fd28a6454172ad7ed74bae49"
   },
   "source": [
    "Define metrics and loss function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "23672e7c-a61a-40cb-9221-5d52a3893cb1",
    "_uuid": "bfaf03f19a053db96548e4973a26060072c3ccce",
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "from keras import backend as K\n",
    "from keras.losses import binary_crossentropy\n",
    "\n",
    "\n",
    "def mean_iou(y_true, y_pred):\n",
    "    prec = []\n",
    "    for t in np.arange(0.5, 1.0, 0.05):\n",
    "        y_pred_ = tf.to_int32(y_pred > t)\n",
    "        score, up_opt = tf.metrics.mean_iou(y_true, y_pred_, 2)\n",
    "        K.get_session().run(tf.local_variables_initializer())\n",
    "        with tf.control_dependencies([up_opt]):\n",
    "            score = tf.identity(score)\n",
    "        prec.append(score)\n",
    "    return K.mean(K.stack(prec))\n",
    "\n",
    "def dice_coef(y_true, y_pred):\n",
    "    smooth = 1.\n",
    "    y_true_f = K.flatten(y_true)\n",
    "    y_pred_f = K.flatten(y_pred)\n",
    "    intersection = K.sum(y_true_f * y_pred_f)\n",
    "    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n",
    "\n",
    "def bce_dice_loss(y_true, y_pred):\n",
    "    return 0.5 * binary_crossentropy(y_true, y_pred) - dice_coef(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "af47f36d-64d0-4f71-a012-8761055e0ea2",
    "_uuid": "fa38f24d8a66d3017332927be0f350a00b389558",
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from skimage.morphology import label\n",
    "\n",
    "def rle_encoding(x):\n",
    "    dots = np.where(x.T.flatten() == 1)[0]\n",
    "    run_lengths = []\n",
    "    prev = -2\n",
    "    for b in dots:\n",
    "        if (b>prev+1): run_lengths.extend((b + 1, 0))\n",
    "        run_lengths[-1] += 1\n",
    "        prev = b\n",
    "    return run_lengths\n",
    "\n",
    "def prob_to_rles(x, cutoff=0.5):\n",
    "    lab_img = label(x > cutoff)\n",
    "    for i in range(1, lab_img.max() + 1):\n",
    "        yield rle_encoding(lab_img == i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "9b52df95-5431-4765-a304-fb3192fb25ac",
    "_uuid": "1b752a957409a3242f07436af05572f8bfe5ad2d",
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import os\n",
    "import pandas as pd\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    img_size = 256\n",
    "    batch_size = 32\n",
    "    train_path = '../input/stage1_train/'\n",
    "    test_path = '../input/stage1_test/'\n",
    "    \n",
    "    X_train, Y_train, X_test, sizes_test = make_df(train_path, test_path, img_size)\n",
    "    xtr, xval, ytr, yval = train_test_split(X_train, Y_train, test_size=0.1, random_state=7)\n",
    "    train_generator, val_generator = generator(xtr, xval, ytr, yval, batch_size)\n",
    "    \n",
    "    model = Unet(img_size)\n",
    "    model.compile(optimizer='adam', loss=bce_dice_loss, metrics=[mean_iou])\n",
    "    \n",
    "    model.fit_generator(train_generator, steps_per_epoch=len(xtr)/6, epochs=50,\n",
    "                        validation_data=val_generator, validation_steps=len(xval)/batch_size)\n",
    "    \n",
    "    preds_test = model.predict(X_test, verbose=1)\n",
    "\n",
    "    preds_test_upsampled = []\n",
    "    for i in range(len(preds_test)):\n",
    "        preds_test_upsampled.append(cv2.resize(preds_test[i], \n",
    "                                           (sizes_test[i][1], sizes_test[i][0])))\n",
    "        \n",
    "    test_ids = next(os.walk(test_path))[1]\n",
    "    new_test_ids = []\n",
    "    rles = []\n",
    "    for n, id_ in enumerate(test_ids):\n",
    "        rle = list(prob_to_rles(preds_test_upsampled[n]))\n",
    "        rles.extend(rle)\n",
    "        new_test_ids.extend([id_] * len(rle))\n",
    "    sub = pd.DataFrame()\n",
    "    sub['ImageId'] = new_test_ids\n",
    "    sub['EncodedPixels'] = pd.Series(rles).apply(lambda x: ' '.join(str(y) for y in x))\n",
    "    sub.to_csv('sub.csv', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "eb2418cf-bfd1-4679-b133-a8bc0cb8b31e",
    "_uuid": "e9cb41031c9abf8bde6c4819c22cdc0ac10d323d",
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
